

#ifndef __datorH__
#define __datorH__

#include "trigger.h"
#include "data.h"

#define META_WRAP(a) a

namespace WtfEngine
{
	template <class tType>
	struct DatorProxy {
		typedef DatorProxy<tType>	tThis;
		
		tType*	pValue;
		
		DatorProxy(tType& v) : pValue(&v) {};
		
		void Init(const tType& v){
			*pValue = v;
		};
		
		/**** Delegates ****/
		
		/// Assignment
		inline void operator = (const tThis& v){
			Init(*(v.pValue));
		};
		inline void operator = (const tType& v) {
			Init(v);
		};

		DELEGATE_OPERATORS_IN_TPL(tType, *pValue);
	};
	
	template <class tType>
	struct Dator {
		typedef Dator<tType>	tThis;
		
		tType	Value;
		
		Dator() {};
		explicit Dator(const tType& v) : Value(v) {};
		
		virtual void Init(const tType& v) {
			Value = v;
		};
		
		/**** Delegates ****/
		
		/// Assignment
		inline void operator = (const tThis& v){
			Init(v.Value);
		};
		inline void operator = (const tType& v) {
			Init(v);
		};

		DELEGATE_OPERATORS_IN_TPL(tType, Value);
		
		operator tType () const {
			return (Value);
		};
	};
	
	
	/**
	 * A dator which fires a trigger when the value is changed.
	 **/
	template <class tType>
	struct TriggerDator : public Dator<tType>
	{
		typedef TriggerDator<tType>	tThis;
		
		TriggerDator() : Dator<tType>() {};
		explicit TriggerDator(const tType& v) : Dator<tType>(v) {};
		
		virtual void Init(const tType& v) {
			Dator<tType>::Init(v);
			Kernel::GetInstance().ProcessEvent(new Trigger<tType, Comparator<tType>::NotEqual>(this->Value,
				typename Meta::Value<tType>::get(this->Value)));
		};
		
		/// Assignment
		inline void operator = (const tThis& v){
			Init(v.Value);
		};
		inline void operator = (const tType& v) {
			Init(v);
		};
	};
	
	template <class tType>
	class Setting : public TriggerDator<tType>, public DataObject
	{
		typedef Setting<tType>	tThis;
		
		public:
			Setting(const StringPool::Entry& sName, const tType& v)
			: TriggerDator<tType>(v), DataObject(sName) {};
		
			virtual ~Setting() {};

			/**
			 * Binds a functor to be executed when the setting's value is
			 * changed.
			 **/
			void BindNotify(const Functor::tRef& f){
				Kernel::GetInstance().GetGameStatePtr()->BindEvent(
					new ChangeTrigger<tType>(this->Value), f);
			};
			
			/// Assignment
			inline void operator = (const tThis& v){
				TriggerDator<tType>::Init(v.Value);
			};
			inline void operator = (const tType& v) {
				TriggerDator<tType>::Init(v);
			};
			
			/// Gets the setting corresponing to the given name
			template <typename T>
			static T* GetType(const String& sName) {
				return DataManager::GetInstance().Load<T>(sName);
			};
			static tThis* Get(const String& sName) {
				return GetType<tThis>(sName);
			};

			/// Sets the specified setting to the specified value, returns a
			/// pointer to the setting
			template <typename T>
			static T* SetType(const String& sName, const tType& v) {
				T * pSetting;
				if((pSetting = DataManager::GetInstance().Get<T>(sName))) {
					// Setting already exists - change value
					*pSetting = v;
				} else {
					DataManager::GetInstance().Set<T>(pSetting = new T(sName, v));
				};
				return pSetting;
			};
			
			inline static tThis* Set(const String& sName, const tType& v) {
				return SetType<tThis>(sName, v);
			};

			/*static Setting<tType>* Load(const String& sName) {
				
			};*/
			
			DATA_LOADABLE(tThis, sizeof(tThis));
	};
};
namespace Meta {
	/// Adaptor to fetch values from Dators
	template <class T> META_VALUE(WtfEngine::DatorProxy<T>, T, *(that.pValue));
	template <class T> META_VALUE(WtfEngine::Dator<T>, T, that.Value);
	template <class T> META_VALUE_DEFAULT(WtfEngine::TriggerDator<T>, WtfEngine::Dator<T>);
	template <class T> META_VALUE_DEFAULT(WtfEngine::Setting<T>, WtfEngine::TriggerDator<T>);
};

#endif
